package com.physics.mps.module.comsol.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.physics.mps.common.config.properties.ApiProperties;
import com.physics.mps.common.exception.BusinessException;
import com.physics.mps.common.util.AssertUtil;
import com.physics.mps.common.util.MinioUtil;
import com.physics.mps.common.util.OkHttpUtil;
import com.physics.mps.entity.dto.SubTaskEntity;
import com.physics.mps.entity.dto.TaskComputeResultEntity;
import com.physics.mps.entity.enums.CodeEnum;
import com.physics.mps.entity.enums.ScriptStatusEnum;
import com.physics.mps.entity.vo.api.Result;
import com.physics.mps.entity.vo.api.UploadVo;
import com.physics.mps.module.compute.TaskComputeResultService;
import com.physics.mps.module.comsol.AsyncHandler;
import com.physics.mps.module.comsol.builder.Builder;
import com.physics.mps.module.comsol.builder.common.CommonBuilder;
import com.physics.mps.module.comsol.builder.comp.CompBuilder;
import com.physics.mps.module.comsol.builder.geom.GeomBuilder;
import com.physics.mps.module.comsol.builder.mat.MaterialBuilder;
import com.physics.mps.module.comsol.builder.mesh.MeshBuilder;
import com.physics.mps.module.comsol.builder.physics.PhysicsBuilder;
import com.physics.mps.module.comsol.builder.result.ResultBuilder;
import com.physics.mps.module.comsol.builder.std.StdBuilder;
import com.physics.mps.module.comsol.enums.PhysicsEnum;
import com.physics.mps.module.comsol.enums.SolveEnum;
import com.physics.mps.module.comsol.enums.StudyEnum;
import com.physics.mps.module.comsol.service.ComsolService;
import com.physics.mps.module.comsol.vo.*;
import com.physics.mps.module.task.sub.SubTaskService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;


/**
 * @author mcsoft
 * @date 2023/10/16 16:18
 * @description comsol service impl
 */
@Log4j2
@RequiredArgsConstructor
@Service
public class ComsolServiceImpl implements ComsolService {

    private final SubTaskService subTaskService;

    private final TaskComputeResultService taskComputeResultService;

    private final AsyncHandler asyncHandler;

    private final ApiProperties apiProperties;

    private final MinioUtil minioUtil;


    @Value("${simulate.comsol.workspace}")
    private String workspace;


    @Override
    public void submit(ComsolSolveVo comsolSolveVo) {
        String className = comsolSolveVo.getClassName();

        // 生成comsol java 脚本
        genJavaFile(comsolSolveVo);

        if (comsolSolveVo.isNoRun()) {
            return;
        }

        // TODO taskId
        String taskId = IdUtil.fastSimpleUUID();

        // 子任务未提前创建则先创建，否则关联已创建的子任务
        if (StringUtils.isEmpty(comsolSolveVo.getSubTaskId())) {
            // 创建子任务
            String subTaskId = subTaskService.addSubTask(comsolSolveVo.getAppId(), comsolSolveVo.getAppModuleId()
                    , taskId, comsolSolveVo.getTaskName(), comsolSolveVo.getStpPath());
            comsolSolveVo.setSubTaskId(subTaskId);
        }

        asyncHandler.exec(() -> {
            // running
            SubTaskEntity subTaskEntity = subTaskService.getBaseMapper().selectById(comsolSolveVo.getSubTaskId());
            subTaskEntity.setStatus(ScriptStatusEnum.RUNNING.getStatus());
            subTaskService.updateById(subTaskEntity);


            // 求解日志文件
            String logFilePath = String.format(LOG_FILE_PATH_FORMAT, workspace, className);
            try {
                // 求解 编译 & 运行
                String errorInfo = solution(className);
                if (errorInfo != null) {
                    throw new RuntimeException(errorInfo);
                }

                // 求解结果文件
                String resultFilePath = String.format(RESULT_FILE_PATH_FORMAT, workspace, className);
                // 求解结果图片
                String resultImgPath = String.format(RESULT_IMG_FILE_PATH_FORMAT, workspace, className);
                // 派生值文件路径
                String volumePath = volumeJson(className, comsolSolveVo.getStudyVo().getSolveEnum());

                // 上传结果文件
                String url = upload2FileSystem(resultFilePath);
                // 上传图片结果
                String picUrl = upload2FileSystem(resultImgPath);
                // 派升值url
                String volumeUrl = upload2FileSystem(volumePath);

                // 更新子任务
                subTaskService.updateTask(comsolSolveVo.getSubTaskId(), logFilePath, resultFilePath, resultImgPath, url, picUrl, volumeUrl);
            } catch (Exception e) {
                String message = e.getMessage();
                if (!message.contains(COMSOL_COMPILE_ERROR)) {
                    // 求解失败
                    subTaskEntity.setLogPath(logFilePath);
                }
                subTaskEntity.setStatus(ScriptStatusEnum.EXCEPTION.getStatus());
                subTaskEntity.setCmdLog(message);
                subTaskService.updateById(subTaskEntity);
            }
        });
    }


    /**
     * 上传到文件服务器
     *
     * @param path
     * @return
     */
    private String upload2FileSystem(String path) {
        String filename = Paths.get(path).getFileName().toString();
        ResponseEntity<String> upload = minioUtil.upload(filename, apiProperties.getModuleName(), path);
        if (upload.getStatusCode() != HttpStatus.OK) {
            throw new BusinessException(CodeEnum.TASK_FILE_UPLOAD_FAILED);
        }
        return upload.getBody();
    }


    /**
     * 上传结果文件
     *
     * @param local
     * @return
     */
    private String uploadResultFile(String local) {
        Map<String, String> params = new HashMap<>(8);
        File file = new File(local);

        Map<String, String> urls = apiProperties.getUrls();
        String url = urls.get("upload-file");
        params.put("moduleName", apiProperties.getModuleName());
        params.put("accessKey", apiProperties.getAccessKey());
        params.put("secretKey", apiProperties.getSecretKey());

        try (Response response = OkHttpUtil.uploadFile(url, file, params);
             ResponseBody responseBody = response.body()) {
            AssertUtil.isNull(responseBody, CodeEnum.UPLOAD_EXCEPTION);
            String body = new String(responseBody.bytes());
            Result<UploadVo> result = JSONUtil.toBean(body, new TypeReference<Result<UploadVo>>() {
            }, false);
            AssertUtil.isFalse(CodeEnum.UPLOAD_EXCEPTION, result.getCode() == 200);
            UploadVo resultData = result.getData();
            return resultData.getUrl();
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
    }


    /**
     * 派生值文件路径
     *
     * @param className
     * @return
     */
    @SneakyThrows
    private String volumeJson(String className, SolveEnum solveEnum) {
        String targetPath = String.format(RESULT_VOLUME_PATH_FORMAT, workspace, className, "json");

        try (
                OutputStream outputStream = Files.newOutputStream(Paths.get(targetPath))
        ) {
            JSONObject result = read2Json(className, solveEnum);
            outputStream.write(JSONUtil.toJsonStr(result).getBytes());
            outputStream.flush();
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
        return targetPath;
    }


    /**
     * txt -> json
     *
     * @param className
     * @return
     */
    @SneakyThrows
    private JSONObject read2Json(String className, SolveEnum solveEnum) {
        String sourcePath = String.format(RESULT_VOLUME_PATH_FORMAT, workspace, className, "txt");
        JSONObject result = JSONUtil.createObj();
        JSONObject solve = JSONUtil.createObj();
        if (SolveEnum.RandomVibration.equals(solveEnum)) {
            result.set("vibration", solve);
        } else {
            result.set("heatTransfer", solve);
        }
        /**
         * component:器件名称1:293.8615973101125:293.1522133049206
         * board:板名称1:0.0:0.0
         */
        try (
                InputStream inputStream = Files.newInputStream(Paths.get(sourcePath));
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "GBK"));
        ) {
            JSONArray component = new JSONArray();
            JSONArray board = new JSONArray();
            solve.set("component", component);
            solve.set("board", board);
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                String[] split = line.split(":");
                JSONObject obj = JSONUtil.createObj();
                String type = split[0];
                if (Objects.equals(type, "component")) {
                    component.add(obj);
                } else if (Objects.equals(type, "board")) {
                    board.add(obj);
                } else {
                    continue;
                }
                obj.set("name", split[1]);
                if (SolveEnum.RandomVibration.equals(solveEnum)) {
                    obj.set("distance", split[2]);
                } else {
                    obj.set("maxTemperature", split[2]);
                    obj.set("minTemperature", split[3]);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new RuntimeException(e);
        }
        return result;
    }


    @SneakyThrows
    @Override
    public void submit(ComsolVo comsolVo) {
        log.info("=== Comsol 仿真主程序发送参数{} ===", JSONUtil.toJsonStr(comsolVo));
        String stpPath = comsolVo.getStpPath();
        File file = FileUtil.file(stpPath);
        // Path path = Paths.get(stpPath);
        // 本地绝对路径
        // 因为主程序下载链接带token,影响stp文件命名，所以再切分
        String[] split = file.getName().split("&&");
        String local = workspace + "/stp/" + split[0];
        if (!StringUtils.startsWith(stpPath, "http")) {
            // 不是 url，则为本地绝对路径
            comsolVo.setStpPath(local);
            submit(convert(comsolVo));
            return;
        }
        CountDownLatch countDownLatch = new CountDownLatch(1);
        asyncHandler.exec(() -> {
            // 下载 stp 到本地
            log.info("=== comsol stp下载路径 {} === comsol stp文件名 {}", stpPath, local);
            OkHttpUtil.download(stpPath, local);
            countDownLatch.countDown();
        });
        ComsolSolveVo comsolSolveVo = convert(comsolVo);
        comsolSolveVo.setStpPath(local);
        countDownLatch.await();
        submit(comsolSolveVo);
    }

    @Override
    public void genJavaFile(ComsolSolveVo vo) {
        String className = vo.getClassName();

        Builder builder = new Builder(workspace);
        builder.start(className);

        // 组件
        builder.echo("组件");
        String compTag = builder.uniqueTag("comp");
        CompBuilder compBuilder = new CompBuilder(builder);
        compBuilder.build(compTag);


        // 全局
        builder.echo("全局");
        CommonBuilder commonBuilder = new CommonBuilder(builder);
        commonBuilder.buildCommonFeature(vo.getCommonFeatureVo());
        commonBuilder.buildFunction(vo.getFunctionVos());

        // 组件几何
        builder.echo("组件几何");
        String geomTag = builder.uniqueTag("geom");
        GeomBuilder geomBuilder = new GeomBuilder(builder);
        geomBuilder.build(geomTag, vo.getStpPath(), vo.getGeomVo(), vo.getPairVos());

        // 物理场
        builder.echo("物理场");
        PhysicsBuilder physicsBuilder = new PhysicsBuilder(builder);
        physicsBuilder.build(geomTag, vo.getPhysics());
        physicsBuilder.buildMultiPhysics(vo.getMultiPhysicsVo());

        // 材料
        builder.echo("材料");
        MaterialBuilder materialBuilder = new MaterialBuilder(builder);
        materialBuilder.build(geomTag, vo.getMaterials());

        // 网格
        builder.echo("网格");
        MeshBuilder meshBuilder = new MeshBuilder(builder);
        meshBuilder.build(geomTag, vo.getMeshes());


        // 研究
        builder.echo("研究");
        StdBuilder stdBuilder = new StdBuilder(builder);
        stdBuilder.build(vo.getStudyVo(), vo.getMap());

        // 结果
        builder.echo("结果");
        ResultBuilder resultBuilder = new ResultBuilder(builder);
        ResultVo resultVo = vo.getResultVo();

        // vtu
        resultBuilder.buildVtu(resultVo.getResultFile());

        // 图片
        String pgTag = resultBuilder.buildPlotGroup3D(vo.getResultVo());
        resultBuilder.buildImg(pgTag, resultVo.getImgFile());

        // 体最大值、最小值

        List<Volume> volumes = resultVo.getVolumes();
        if (!CollectionUtils.isEmpty(volumes)) {
            String volumeResultPath = String.format(RESULT_VOLUME_PATH_FORMAT, workspace, className, "txt");
            resultBuilder.buildVolume(geomTag, vo.getStudyVo().getSolveEnum(), volumeResultPath, resultVo);
        }

        builder.end();

        builder.toFile(String.format(JAVA_FILE_PATH_FORMAT, workspace, className));
    }

    @SneakyThrows
    @Override
    public String solution(String clasName) {
        boolean result = false;
        Process compile = RuntimeUtil.exec(String.format(EXEC_CMD_FORMAT, workspace, workspace, clasName));

        StringBuilder echo = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(compile.getInputStream(), "GBK"));) {
            String line = null;
            while ((line = reader.readLine()) != null) {
                echo.append(line);
                echo.append("\n");
                if (line.startsWith(COMSOL_ERROR)) {
                    log.error("comsol solution error, {}", line);
                    break;
                } else if (line.startsWith(COMSOL_SUCCESS)) {
                    result = true;
                    break;
                }
            }
        }
        if (result) {
            return null;
        }
        return echo.toString();
    }


    @Override
    public String getResult(String subTaskId, String type) {
        // resultId = subTaskId
        TaskComputeResultEntity taskComputeResult = taskComputeResultService.getById(subTaskId);
        AssertUtil.isNull(taskComputeResult, CodeEnum.RESULT_FILE_NOT_EXIST);
        switch (type) {
            case "file": {
                return taskComputeResult.getFilePath();
            }
            case "img": {
                return taskComputeResult.getPicPath();
            }
        }
        return null;
    }

    /**
     * 按模块转换格式
     *
     * @param comsolVo
     * @return
     */
    private ComsolSolveVo convert(ComsolVo comsolVo) {
        ComsolSolveVo comsolSolveVo = null;
        switch (comsolVo.getAppModuleId()) {
            case RandomVibration: {
                log.info("RandomVibration");
                comsolSolveVo = randomVibration(comsolVo);
                break;
            }
            case HeatTransferInSolids: {
                log.info("HeatTransferInSolids");
                comsolSolveVo = heatTransferInSolids(comsolVo);
                StudyVo studyVo = comsolSolveVo.getStudyVo();
                studyVo.setStudyEnum(StudyEnum.Transient);
                studyVo.setSolveEnum(SolveEnum.HeatTransferInSolids);
                List<String> uris = new ArrayList<>();
                uris.add("/physics/ht");
                studyVo.setSolveList(uris);
                break;
            }
            case ThermalStructureInteraction: {
                log.info("ThermalStructureInteraction");
                comsolSolveVo = thermalStructureInteraction(comsolVo);
                StudyVo studyVo = comsolSolveVo.getStudyVo();
                studyVo.setStudyEnum(StudyEnum.Transient);
                studyVo.setSolveEnum(SolveEnum.ThermalStructureInteraction);
                List<String> uris = new ArrayList<>();
                uris.add("/physics/solid");
                uris.add("/physics/ht");
                uris.add("/multiphysics/ThermalExpansion");
                studyVo.setSolveList(uris);
                break;
            }
            case HeatTransferInSolidsAndFluids: {
                log.info("HeatTransferInSolidsAndFluids");
                comsolSolveVo = heatTransferInSolidsAndFluids(comsolVo);
                StudyVo studyVo = comsolSolveVo.getStudyVo();
                studyVo.setStudyEnum(StudyEnum.Transient);
                studyVo.setSolveEnum(SolveEnum.HeatTransferInSolidsAndFluids);
                List<String> uris = new ArrayList<>();
                uris.add("/physics/ht");
                studyVo.setSolveList(uris);
                break;
            }
        }
        return comsolSolveVo;
    }


    /**
     * 随机振动 格式转换
     *
     * @param comsolVo
     * @return
     */
    private ComsolSolveVo randomVibration(ComsolVo comsolVo) {
        ComsolSolveVo comsolSolveVo = commonConvert(comsolVo);
        List<SettingVo> settings = comsolVo.getSettings();

        // 全局输入
        commonSetting(comsolSolveVo, settings);

        // 单物理场特征
        singlePhysicsSetting(comsolSolveVo, settings, PhysicsEnum.SolidMechanics.getPhysics());
        List<PhysicsVo> physics = comsolSolveVo.getPhysics();
        if (!CollectionUtils.isEmpty(physics)) {
            PhysicsVo physicsVo = physics.get(0);
            // 重力
            List<PhysicsFeature> collect = physicsVo.getFeatures().stream().filter(f -> "Gravity".equals(f.getFeature())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(collect)) {
                PhysicsFeature physicsFeature = collect.get(0);
                physicsFeature.setExtraProperty("set(\"g\", new String[]{\"-accX\", \"-accY\", \"-accZ\"});");
                physicsFeature.setEntityDim(3);
                physicsFeature.setSelectAll(true);
            }

            // 阻尼
            collect = physicsVo.getFeatures().stream().filter(f -> "Damping".equals(f.getFeature())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(collect)) {
                PhysicsFeature physicsFeature = collect.get(0);
                // 默认生成的线弹性材料1
                physicsFeature.setParentFeatureTag("lemm1");
                physicsFeature.setEntityDim(3);
                physicsFeature.setSelectAll(true);
            }
        }


        // 结果
        ResultVo resultVo = comsolSolveVo.getResultVo();
        /**
         * rvib1 随机振动
         * solid.mises_rv 应力
         */
        resultVo.setExpression("rvib1.q2sq(solid.mises_rv)");
        // 求解2
        resultVo.setDatasetTag("dset2");

        resultVo.setVolumeExpression("rvib1.q2sq(solid.edeve)");

        // 研究
        stdSetting(comsolSolveVo, settings);
        StudyVo studyVo = comsolSolveVo.getStudyVo();
        studyVo.setStudyEnum(StudyEnum.PSD);

        return comsolSolveVo;
    }


    /**
     * 固体传热 格式转换
     *
     * @param comsolVo
     * @return
     */
    private ComsolSolveVo heatTransferInSolids(ComsolVo comsolVo) {
        ComsolSolveVo comsolSolveVo = commonConvert(comsolVo);
        List<SettingVo> settings = comsolVo.getSettings();

        // 单物理场特征
        singlePhysicsSetting(comsolSolveVo, settings, PhysicsEnum.HeatTransfer.getPhysics());

        // 研究
        stdSetting(comsolSolveVo, settings);

        // 结果
        ResultVo resultVo = comsolSolveVo.getResultVo();
        resultVo.setExpression("T");
        resultVo.setDatasetTag("dset1");

        resultVo.setVolumeExpression("T");

        return comsolSolveVo;
    }


    /**
     * 热固偶尔 格式转换
     *
     * @param comsolVo
     * @return
     */
    private ComsolSolveVo thermalStructureInteraction(ComsolVo comsolVo) {
        ComsolSolveVo comsolSolveVo = commonConvert(comsolVo);
        List<SettingVo> settings = comsolVo.getSettings();

        // 多物理场特征
        multiPhysicsSetting(comsolSolveVo, settings);

        MultiPhysicsVo multiPhysicsVo = new MultiPhysicsVo();
        multiPhysicsVo.setMultiPhysics("ThermalExpansion");
        List<PhysicsEnum> physicsList = new ArrayList<>();
        physicsList.add(PhysicsEnum.HeatTransfer);
        physicsList.add(PhysicsEnum.SolidMechanics);
        multiPhysicsVo.setPhysicsList(physicsList);
        comsolSolveVo.setMultiPhysicsVo(multiPhysicsVo);

        // 研究
        stdSetting(comsolSolveVo, settings);

        // 结果
        ResultVo resultVo = comsolSolveVo.getResultVo();
        resultVo.setExpression("solid.mises");
        resultVo.setDatasetTag("dset1");

        resultVo.setVolumeExpression("T");

        return comsolSolveVo;
    }


    /**
     * 固体流体传热 格式转换
     *
     * @param comsolVo
     * @return
     */
    private ComsolSolveVo heatTransferInSolidsAndFluids(ComsolVo comsolVo) {
        ComsolSolveVo comsolSolveVo = commonConvert(comsolVo);
        List<SettingVo> settings = comsolVo.getSettings();
        // 单物理场特征
        singlePhysicsSetting(comsolSolveVo, settings, PhysicsEnum.HeatTransferInSolidsAndFluids.getPhysics());

        // 研究
        stdSetting(comsolSolveVo, settings);

        // 结果
        ResultVo resultVo = comsolSolveVo.getResultVo();
        resultVo.setExpression("T");
        resultVo.setDatasetTag("dset1");

        resultVo.setVolumeExpression("T");

        return comsolSolveVo;
    }


    /**
     * 公共 格式转换
     *
     * @param comsolVo
     * @return
     */
    private ComsolSolveVo commonConvert(ComsolVo comsolVo) {
        ComsolSolveVo comsolSolveVo = new ComsolSolveVo(comsolVo);

        String className = "Model" + IdUtil.getSnowflakeNextId();
        comsolSolveVo.setClassName(className);

        // 具体配置
        List<SettingVo> settings = comsolVo.getSettings();
        if (CollectionUtils.isEmpty(settings)) {
            return comsolSolveVo;
        }

        // 几何配置
        geomSetting(comsolSolveVo, settings);

        // 材料配置
        matSetting(comsolSolveVo, settings);

        // 单网格配置
        singleMeshSetting(comsolSolveVo, settings);

        // 结果配置
        resultSetting(comsolSolveVo);

        // 体最大值最小值
        volumeSetting(comsolSolveVo, settings);

        return comsolSolveVo;
    }


    /**
     * 全局输入 格式转换
     *
     * @param comsolSolveVo
     * @param settings
     */
    private void commonSetting(ComsolSolveVo comsolSolveVo, List<SettingVo> settings) {
        SettingVo setting;
        List<ParamVo> appParams;
        ParamVo param;
        Map<String, String> params;
        List<SettingVo> commonSettings = settings.stream().filter(s -> StringUtils.startsWith(s.getGroupKey(), "common_")).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(commonSettings)
                && (setting = commonSettings.get(0)) != null
                && !CollectionUtils.isEmpty((appParams = setting.getAppParams()))
                && (param = appParams.get(0)) != null
                && !CollectionUtils.isEmpty(params = param.getParams())) {

            String groupKey = setting.getGroupKey();
            String definitions = groupKey.replace("common_", "");
            List<String> names = new ArrayList<>();
            List<String> expressions = new ArrayList<>();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                names.add(entry.getKey());
                expressions.add(entry.getValue());
            }
            CommonFeatureVo commonFeatureVo = new CommonFeatureVo();
            commonFeatureVo.setDefinitions(definitions);
            commonFeatureVo.setNames(names);
            commonFeatureVo.setExpressions(expressions);
            comsolSolveVo.setCommonFeatureVo(commonFeatureVo);
        }


        List<SettingVo> funcSettings = settings.stream().filter(s -> StringUtils.startsWith(s.getGroupKey(), "func_")).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(funcSettings)) {
            return;
        }

        List<FunctionVo> functionVos = new ArrayList<>();
        for (SettingVo funcSetting : funcSettings) {
            String groupKey = funcSetting.getGroupKey();
            String[] splits = groupKey.split("_");
            String func = splits[1];
            String tableType = splits[2];

            if (CollectionUtils.isEmpty((appParams = funcSetting.getAppParams()))
                    || (param = appParams.get(0)) == null) {
                continue;
            }
            FunctionVo functionVo = new FunctionVo();
            functionVo.setFunc(func);
            functionVo.setTableType(tableType);

            List<List<String>> table = new ArrayList<>();
            for (ParamVo appParam : appParams) {
                Map<String, String> tempMap = appParam.getParams();
                if (CollectionUtils.isEmpty(tempMap)) {
                    continue;
                }
                List<String> tableItem = new ArrayList<>();
                for (Map.Entry<String, String> entry : tempMap.entrySet()) {
                    tableItem.add(entry.getValue());
                    if ("Piecewise".equals(func)) {
                        if ("fLow".equals(entry.getKey())) {
                            comsolSolveVo.getMap().put("fLow", entry.getValue());
                            continue;
                        }
                        if ("fHigh".equals(entry.getKey())) {
                            comsolSolveVo.getMap().put("fHigh", entry.getValue());
                        }
                    }
                }
                // 分段
                if ("Piecewise".equals(func)) {
                    tableItem.add("exp(int1(log(x[1/Hz])))");
                }
                table.add(tableItem);
            }
            functionVo.setTable(table);
            functionVos.add(functionVo);
        }
        comsolSolveVo.setFunctionVos(functionVos);
    }


    /**
     * 结果 格式转换
     *
     * @param comsolSolveVo
     */
    private void resultSetting(ComsolSolveVo comsolSolveVo) {
        // 结果文件
        String resultFile = String.format(RESULT_FILE_PATH_FORMAT, workspace, comsolSolveVo.getClassName());
        String imgFile = String.format(RESULT_IMG_FILE_PATH_FORMAT, workspace, comsolSolveVo.getClassName());

        ResultVo resultVo = new ResultVo();
        resultVo.setResultFile(resultFile);
        resultVo.setImgFile(imgFile);
        comsolSolveVo.setResultVo(resultVo);
    }


    /**
     * 几何 格式转换
     *
     * @param comsolSolveVo
     * @param settings
     */
    private void geomSetting(ComsolSolveVo comsolSolveVo, List<SettingVo> settings) {
        // 几何配置
        List<SettingVo> geomSettings = settings.stream().filter(s -> "geom".equals(s.getGroupKey())).collect(Collectors.toList());
        SettingVo setting;
        List<ParamVo> appParams;
        ParamVo param;
        if (!CollectionUtils.isEmpty(geomSettings)
                && (setting = geomSettings.get(0)) != null
                && !CollectionUtils.isEmpty((appParams = setting.getAppParams()))
                && (param = appParams.get(0)) != null) {
            // 只有一个配置
            Map<String, String> params = param.getParams();
            GeomVo geomVo = new GeomVo();
            geomVo.setAction(params.getOrDefault("action", "union"));
            geomVo.setImportTol(Double.parseDouble(params.getOrDefault("importtol", "1E-6")));
            geomVo.setRepairTol(Double.parseDouble(params.getOrDefault("repairtol", "1.2E-6")));
            geomVo.setCreatePairs(Boolean.parseBoolean(params.getOrDefault("createpairs", "true")));
            comsolSolveVo.setGeomVo(geomVo);
        }

        // 一致边界对
        List<SettingVo> pairSettings = settings.stream().filter(s -> "pair".equals(s.getGroupKey())).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(pairSettings)
                && (setting = pairSettings.get(0)) != null
                && !CollectionUtils.isEmpty((appParams = setting.getAppParams()))) {
            List<PairVo> pairVos = new ArrayList<>(appParams.size());
            for (ParamVo appParam : appParams) {
                pairVos.add(new PairVo(appParam.getCoordinate(), appParam.getCoordinate2()));
            }
            comsolSolveVo.setPairVos(pairVos);
        }
    }


    /**
     * 研究 格式转换
     *
     * @param comsolSolveVo
     * @param settings
     */
    private void stdSetting(ComsolSolveVo comsolSolveVo, List<SettingVo> settings) {
        // 研究配置
        List<SettingVo> stdSettings = settings.stream().filter(s -> "std".equals(s.getGroupKey())).collect(Collectors.toList());
        SettingVo setting;
        List<ParamVo> appParams;
        ParamVo param;
        StudyVo studyVo = new StudyVo();
        if (!CollectionUtils.isEmpty(stdSettings)
                && (setting = stdSettings.get(0)) != null
                && !CollectionUtils.isEmpty((appParams = setting.getAppParams()))
                && (param = appParams.get(0)) != null) {
            // 只有一个配置
            Map<String, String> params = param.getParams();
            studyVo.setParams(params);

            // TODO
            studyVo.setTList(params.getOrDefault("tlist", "1"));
        } else {
            studyVo.setTList("range(0,0.1,1)");
        }
        comsolSolveVo.setStudyVo(studyVo);

    }


    /**
     * 材料 格式转换
     *
     * @param comsolSolveVo
     * @param settings
     */
    private void matSetting(ComsolSolveVo comsolSolveVo, List<SettingVo> settings) {
        List<SettingVo> matSettings = settings.stream().filter(s -> "default_mat".equals(s.getGroupKey())).collect(Collectors.toList());
        SettingVo setting;
        List<ParamVo> appParams;
        ParamVo paramVo;
        List<MaterialVo> materialVos = new ArrayList<>();

        if (!CollectionUtils.isEmpty(matSettings)
                && (setting = matSettings.get(0)) != null
                && !CollectionUtils.isEmpty((appParams = setting.getAppParams()))
                && (paramVo = appParams.get(0)) != null) {
            MaterialVo materialVo = new MaterialVo(paramVo.getEntityDim(), paramVo.getCoordinate(), paramVo.getParams());
            materialVo.setSelectAll(true);
            materialVos.add(materialVo);
        }

        matSettings = settings.stream().filter(s -> "mat".equals(s.getGroupKey())).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(matSettings)
                && (setting = matSettings.get(0)) != null
                && !CollectionUtils.isEmpty((appParams = setting.getAppParams()))) {
            appParams.forEach(p -> {
                materialVos.add(new MaterialVo(p.getEntityDim(), p.getCoordinate(), p.getParams()));
            });
            comsolSolveVo.setMaterials(materialVos);
        }

    }


    /**
     * 单网格 格式转换
     *
     * @param comsolSolveVo
     * @param settings
     */
    private void singleMeshSetting(ComsolSolveVo comsolSolveVo, List<SettingVo> settings) {
        List<MeshVo> meshVos = new ArrayList<>();
        MeshVo meshVo = new MeshVo();
        meshVos.add(meshVo);

        List<SettingVo> globalMeshSettings = settings.stream().filter(s -> "global_mesh".equals(s.getGroupKey())).collect(Collectors.toList());
        SettingVo setting;
        List<ParamVo> appParams;
        ParamVo param;
        if (!CollectionUtils.isEmpty(globalMeshSettings)
                && (setting = globalMeshSettings.get(0)) != null
                && !CollectionUtils.isEmpty((appParams = setting.getAppParams()))
                && (param = appParams.get(0)) != null) {
            Map<String, String> params = param.getParams();
            meshVo.setMax(Double.parseDouble(params.getOrDefault("max", "0")));
            meshVo.setMin(Double.parseDouble(params.getOrDefault("min", "0")));
        }

        List<SettingVo> meshSettings = settings.stream().filter(s -> "mesh".equals(s.getGroupKey())).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(meshSettings)
                && (setting = meshSettings.get(0)) != null
                && !CollectionUtils.isEmpty((appParams = setting.getAppParams()))) {
            List<SizeVo> sizeVos = new ArrayList<>();
            appParams.forEach(p -> {
                SizeVo sizeVo = new SizeVo();
                sizeVo.setCoordinate(p.getCoordinate());
                sizeVo.setEntityDim(p.getEntityDim());
                Map<String, String> params = p.getParams();
                sizeVo.setMax(Double.parseDouble(params.getOrDefault("max", "0")));
                sizeVo.setMin(Double.parseDouble(params.getOrDefault("min", "0")));
                sizeVos.add(sizeVo);
            });
            meshVo.setSizes(sizeVos);
        }
        comsolSolveVo.setMeshes(meshVos);
    }


    /**
     * 单物理场 格式转换
     *
     * @param comsolSolveVo
     * @param settings
     * @param physics
     */
    private void singlePhysicsSetting(ComsolSolveVo comsolSolveVo, List<SettingVo> settings, String physics) {
        List<SettingVo> physicsSettings = settings.stream().filter(s -> StringUtils.startsWith(s.getGroupKey(), "physics")).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(physicsSettings)) {
            return;
        }

        // 只有一个物理场
        List<PhysicsVo> physicsVos = new ArrayList<>();
        PhysicsVo physicsVo = new PhysicsVo();
        physicsVo.setPhysics(physics);
        List<PhysicsFeature> features = new ArrayList<>();
        // 遍历物理场特征，属于同一个物理场
        physicsSettings.forEach(phy -> {
            String groupKey = phy.getGroupKey();
            String feature = groupKey.split("_")[1];
            List<ParamVo> params = phy.getAppParams();
            params.forEach(p -> {
                features.add(new PhysicsFeature(feature, p.getEntityDim(), p.getCoordinate(), p.getParams()));
            });
        });
        physicsVo.setFeatures(features);
        physicsVos.add(physicsVo);
        comsolSolveVo.setPhysics(physicsVos);
    }


    /**
     * 多物理场配置
     *
     * @param comsolSolveVo
     * @param settings
     */
    private void multiPhysicsSetting(ComsolSolveVo comsolSolveVo, List<SettingVo> settings) {
        List<SettingVo> physicsSettings = settings.stream().filter(s -> StringUtils.startsWith(s.getGroupKey(), "physics")).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(physicsSettings)) {
            return;
        }
        List<PhysicsVo> physicsVos = new ArrayList<>();
        physicsSettings.forEach(setting -> {
            String groupKey = setting.getGroupKey();
            String[] splits = groupKey.split(SPLIT);
            // 物理场
            String physics = splits[1];
            // 物理场特征（载荷）
            String feature = splits[2];

            // 组装物理场，用于生成脚本
            PhysicsVo physicsVo = getPhysicsVo(physics, physicsVos);
            List<PhysicsFeature> features = physicsVo.getFeatures();
            List<ParamVo> params = setting.getAppParams();
            if (!CollectionUtils.isEmpty(params)) {
                params.forEach(p -> {
                    features.add(new PhysicsFeature(feature, p.getEntityDim(), p.getCoordinate(), p.getParams()));
                });
            }
        });
        comsolSolveVo.setPhysics(physicsVos);
    }


    /**
     * 获取在组装的物理场
     *
     * @param physics
     * @param physicsVos
     * @return
     */
    private PhysicsVo getPhysicsVo(String physics, List<PhysicsVo> physicsVos) {
        PhysicsVo physicsVo = null;
        List<PhysicsVo> collect = physicsVos.stream().filter(phy -> physics.equals(phy.getPhysics())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(collect)) {
            physicsVo = new PhysicsVo();
            physicsVo.setPhysics(physics);
            List<PhysicsFeature> features = new ArrayList<>();
            physicsVo.setFeatures(features);
            physicsVos.add(physicsVo);
        } else {
            physicsVo = collect.get(0);
        }
        return physicsVo;
    }


    /**
     * 体 最大值、最小值
     *
     * @param comsolSolveVo
     * @param settings
     */
    private void volumeSetting(ComsolSolveVo comsolSolveVo, List<SettingVo> settings) {
        List<SettingVo> volumeSettings = settings.stream().filter(s -> StringUtils.startsWith(s.getGroupKey(), "volume")).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(volumeSettings)) {
            return;
        }
        List<Volume> volumes = new ArrayList<>();
        volumeSettings.forEach(setting -> {
            List<ParamVo> appParams;
            if (CollectionUtils.isEmpty((appParams = setting.getAppParams()))) {
                return;
            }
            String type = setting.getGroupKey().split("_")[1];

            appParams.forEach(p -> {
                Volume volume = new Volume(p.getEntityDim(), p.getCoordinate());
                volume.setType(type);
                volume.setName(p.getParams().getOrDefault("name", ""));
                volumes.add(volume);
            });

        });
        comsolSolveVo.getResultVo().setVolumes(volumes);
    }

}
